#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/sched.h>  
#include <asm/io.h>
#include <linux/gpio.h> 
#include <linux/poll.h>
#include "my_key.h"

static struct my_key_dev* my_key = NULL;
/******************************读取数据函数**********************************/
static ssize_t my_key_read(struct file* filp, char __user* buf, size_t size, loff_t* ppos)
{
	struct my_key_dev* dev = filp->private_data;
	int ret;
	char val;
	if (size != sizeof(val)) return -EINVAL; 

	// 如果是非阻塞打开，判断是否有按键按下，有则尝试读取按键值，没有则直接返回错误
	if (filp->f_flags & O_NONBLOCK) {
		if (1 != dev->ev_press)
			return -EAGAIN;
	} else {
		ret = wait_event_interruptible(dev->wq, dev->ev_press); // 阻塞打开，等待被唤醒
		if (signal_pending(current))  // 判断是否是信号引起的唤醒
			ret = -ERESTARTSYS;
		if (0 != ret) return ret;
	}
	val = dev->key_val;
	ret = copy_to_user(buf, &val, sizeof(val)); 
	dev->ev_press = 0;
	if (0 != ret) return -EFAULT;
	return sizeof(val);
}
/*********************按键中断服务函数，在中断的上半段执行***********************/
irqreturn_t my_key_irq_handler(int irq, void* dev)
{
	struct my_key_dev* key = (struct my_key_dev*)dev;
	mod_timer(&key->timer, jiffies + msecs_to_jiffies(10)); // 定时器延时10毫秒
	return IRQ_RETVAL(IRQ_HANDLED);
}
/******************************定时器到期执行函数****************************/
void timer_fun(unsigned long data)
{
	int val = 0;
	struct my_key_dev* dev = (struct my_key_dev*)data;
	val = ioread32(dev->pin.dr_pin);
	val &= GPIO5_1_MASK;
	if (val == GPIO5_1_MASK) 
		dev->key_val = 1;  // 高电平，按键按下
	else
		dev->key_val = 0;  // 低电平，按键松开
	dev->ev_press = 1;
	if (0 != waitqueue_active(&dev->wq))  // 检查是否有进程在等待队列上休眠
		wake_up_interruptible(&dev->wq);  // 唤醒休眠的进程
	if (NULL != dev->fasync)  			  // 检查信号列表中是否进程在等待信号
		kill_fasync (&dev->fasync, SIGIO, POLL_IN);  // 发信号
	dev->key_push_cnt++;
}
/*************************初始化按键相关寄存器***********************/
static void reg_set(struct my_key_dev* dev)
{
	unsigned int val = 0;
	val = ioread32(dev->pin.mux_pin);
	val &= GPIO5_1_MUX_MODE_MASK;
	val |= GPIO5_1_MUX_MODE;
	iowrite32(val, dev->pin.mux_pin);  // 复用为GPIO5_1

	val = ioread32(dev->pin.drir_pin);
	val &= GPIO5_1_MASK;
	iowrite32(val, dev->pin.drir_pin);  // 设置为输入

	val = ioread32(dev->pin.clk_pin);
	val |= GPIO5_CLK_MASK;
	iowrite32(val, dev->pin.clk_pin);  // 开启GPIO5的时钟
}
/*****************打开设备函数，只用于一个进程或者线程打开********************/
static int my_key_open(struct inode* inode, struct file* filp)
{
	unsigned int ret = 0;
	struct my_key_dev* dev = container_of(inode->i_cdev, struct my_key_dev, cdev); 
	// 如果是非阻塞打开，尝试获取互斥体，获取失败直接返回，获取成功继续执行
	if (filp->f_flags & O_NONBLOCK) {
		// mutex_trylock和down_trylock的返回值意义相反
		if (!mutex_trylock(&dev->mutex))
			return -EBUSY;
	}
	else mutex_lock(&dev->mutex);
 
	reg_set(dev);

	// 根据GPIO引脚申请虚拟中断号，IMX_GPIO_NR将GPIO5_1转换成一个32位整数
	dev->irq = gpio_to_irq(IMX_GPIO_NR(5,1));
	// 申请中断，双边沿触发，需要注意的是dev->irq为虚拟中断号，不是硬件中断号
	// 由于GPIO5 0-15引脚共享硬件中断号74，且系统已经使用了GPIO5_3作为心跳led的中断
	// 因此这里还要设置成共享中断
	ret = request_irq(dev->irq, my_key_irq_handler, IRQF_TRIGGER_RISING |   \
				IRQF_TRIGGER_FALLING | IRQF_SHARED, "my_key", dev);
	if (0 != ret) {
		printk(KERN_ERR "request_irq error\n");
		return -EAGAIN;
	}
	filp->private_data = dev;
	printk(KERN_INFO "my_key module open OK\n");
	return 0;
}
/***********************驱动实现的poll函数，用于轮询************************/
static unsigned int my_key_poll(struct file* filep, poll_table* wait)
{
	unsigned int mask = 0;
	struct my_key_dev* dev = filep->private_data;
	poll_wait(filep, &dev->wq, wait);
	if (1 == dev->ev_press)  // 如有按键按下，则表示有数据读取，返回POLLIN和POLLRDNORM标志
		mask = (POLLIN | POLLRDNORM);
	return mask;
}
/*******************驱动实现的fasync函数，实现异步通知功能**********************/
static int my_key_fasync(int fd, struct file* filep, int on)
{
	struct my_key_dev* dev = filep->private_data;
	return fasync_helper(fd, filep, on, &dev->fasync);
}
/*******************释放设备的函数，应用调用close时调用此函数******************/
static int my_key_release(struct inode* inode, struct file* filep)
{
	struct my_key_dev* dev = container_of(inode->i_cdev, struct my_key_dev, cdev); 
	free_irq(dev->irq, dev);  // 释放中断
	my_key_fasync(-1, filep, 0);  // 从异步通知列表中删除filep
	mutex_unlock(&dev->mutex);  // 关闭设备时释放互斥体
	return 0;
}
// 文件操作函数结构体
static const struct file_operations my_key_fops = {
	.owner = THIS_MODULE,
	.open = my_key_open,
	.read = my_key_read,
	.poll = my_key_poll,
	.fasync = my_key_fasync,
	.release = my_key_release,
};
/************************映射控制三个led灯所需的寄存器***************************/
static int key_ioremap(struct my_key_dev* dev)
{
	int ret = 0;
	// 映射GPIO数据寄存器和方向控制寄存器
	dev->pin.dr_pin = ioremap(GPIO5_DR, 4 * 8);
	dev->pin.drir_pin = dev->pin.dr_pin + 1;
	dev->pin.edge_sel_pin = dev->pin.dr_pin + 7;
	// 映射引脚复用控制寄存器和引脚控制寄存器
	dev->pin.mux_pin = ioremap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER1, 4);
	dev->pin.ctl_pin = ioremap(IOMUXC_SNVS_SW_PAD_CTL_PAD_SNVS_TAMPER1, 4);
	// 映射时钟控制寄存器
	dev->pin.clk_pin = ioremap(CCM_CCGR1, 4);
	if ((NULL == dev->pin.dr_pin) || (NULL == dev->pin.mux_pin) || 
	    (NULL == dev->pin.ctl_pin) || (NULL == dev->pin.clk_pin)) {
		ret = -1;
		printk(KERN_ERR "key_ioremap() failed\n"); 
	}
	return ret;	
}
/********************注销已映射的key控制寄存器************************/
static void key_iounmap(struct my_key_dev* dev)
{
	iounmap(dev->pin.dr_pin);
	iounmap(dev->pin.mux_pin);
	iounmap(dev->pin.ctl_pin);
	iounmap(dev->pin.clk_pin);
}
/*******************初始化和注册cdev结构体****************************/
static int set_up_my_key_cdev(struct my_key_dev* dev, int cnt)
{
	int err;
	cdev_init(&dev->cdev, &my_key_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, dev->devno, cnt); // 出错返回负值
	if (err < 0)
		printk(KERN_ERR "adding my_key cdev %d error, errno %d\n", cnt, err);
	return err;
}
/*******************************定义设备的属性********************************/
static ssize_t key_show(struct device* dev, struct device_attribute* attr,char* buf)
{
	return sprintf(buf, "pressed button count %u\n", my_key->key_push_cnt);
} 
// 设备属性文件名为key，属性结构体名称为dev_attr_key，类型为struct device_attribute
// 第二个模式参数要和show store函数匹配，如模式可读可写，则show store函数必须都要提供
static DEVICE_ATTR(key, S_IRUSR | S_IRGRP | S_IROTH, key_show, NULL);

/***************************模块初始化**************************************/
static int __init my_key_init(void)
{
	int ret = 0;
	dev_t devno = 0;
	// 动态分配设备号，传入的devno参数为0,使用unregister_chrdev_region注销动态分配的设备号
	ret = alloc_chrdev_region(&devno, 0, 1, "my_key");
	if (ret < 0) {
		printk(KERN_ERR "alloc_chrdev_region() failed %d\n", ret);
		return ret;	
	}
	// 分配设备结构体内存并将分配的内存清0
	my_key = kzalloc(sizeof(struct my_key_dev), GFP_KERNEL);
	if (NULL == my_key) {
		ret = -ENOMEM;
		printk(KERN_ERR "kzalloc() failed %d\n", ret);
		goto unreg_chrdev;
	}
	my_key->devno = devno;
	my_key->ev_press = 0;
	my_key->key_val = 0;
	init_timer(&my_key->timer);  // 初始化定时器
	my_key->timer.function = timer_fun;
	my_key->timer.data = (unsigned long)my_key;
	add_timer(&my_key->timer);   // 注册定时器
	init_waitqueue_head(&my_key->wq);  // 初始化等待队列头

	// 使用ioremap映射控制key所需的寄存器
	if (0 != key_ioremap(my_key)) { ret = -ENOMEM; goto unreg_chrdev; }

	ret = set_up_my_key_cdev(my_key, 1);
	if (ret < 0) goto iounmap_key;

	// 创建类和设备，当模块加载后会自动在/dev目录下生成设备节点
	my_key->my_key_class = class_create(THIS_MODULE, "my_key_class");
	if (IS_ERR(my_key->my_key_class)) {
		ret = PTR_ERR(my_key->my_key_class);
		printk(KERN_ERR "class_create() failed %d\n", ret);
		goto del_cdev;
	}
	my_key->my_key_device = device_create(my_key->my_key_class, NULL, devno, 
							NULL,"my_key");
	if (IS_ERR(my_key->my_key_device)) {
		ret = PTR_ERR(my_key->my_key_device);
		printk(KERN_ERR "device_create() failed %d\n", ret);
		goto clean_class;
	}
	// 使用device_create_file创建属性文件，一次只能创建一个
	// 使用device_remove_file移除属性文件
	ret = device_create_file(my_key->my_key_device, &dev_attr_key);
	if (ret != 0) goto clean_device;

	// 初始化互斥体
	mutex_init(&my_key->mutex);
	printk(KERN_INFO "my_key module init OK, major %u, minor %u\n", 
					MAJOR(devno), MINOR(devno));
	return 0;
clean_device:
	device_destroy(my_key->my_key_class, devno);
clean_class: 
	class_destroy(my_key->my_key_class);
del_cdev:
	cdev_del(&my_key->cdev);
iounmap_key:	
	key_iounmap(my_key);
	kfree(my_key);
	my_key = NULL;
unreg_chrdev:
	unregister_chrdev_region(devno, 1);
    return ret;
}
/********************模块注销************************/
static void __exit my_key_exit(void)
{
	device_remove_file(my_key->my_key_device, &dev_attr_key);
	device_destroy(my_key->my_key_class, my_key->devno);
	class_destroy(my_key->my_key_class);
	cdev_del(&my_key->cdev);
	del_timer(&my_key->timer);   // 删除定时器
	key_iounmap(my_key);
	unregister_chrdev_region(my_key->devno, 1);
	kfree(my_key);
	my_key = NULL;
	printk(KERN_INFO "my_key module exit\n");
}

module_init(my_key_init);
module_exit(my_key_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyang.plus@foxmail.com");
MODULE_VERSION("v1.00");